#include <math.h>

#include "synchannel.h"
#include "synmixer.h"

extern Synth::Mixer mixer;

namespace Synth {

// Channel parameters
Channel::Channel() {
	
}

// Doing things like this makes it rather impossible to parallellize...
void Channel::ResetChannelParameters() {
	for (int i = 0; i < cp_NumChannelParams; i++) {
		parameterTable[i] = (float)params[i] / 127.f;
		parameterTableInt[i] = params[i];
	}
}

void Channel::DoModulations() {
	parameterTable[ms_ChannelLFO] =
		lfo.GetValue(0,
                     parameterTableInt[cp_ChannelLFOWaveform],
		   	         parameterTableInt[cp_ChannelLFOMode],
					 parameterTable[cp_ChannelLFOSpeed]);
}

void Channel::RenderChannelFX(Block leftbufin, Block rightbufin, Block left_out, Block right_out) {
	// Okay, buf now contains all voices rendered nicely.
	// Time to rock
	if (parameterTableInt[cp_ChannelDistType] != 0) {
		Scale(leftbufin, (1 + parameterTable[cp_ChannelDistPreGain] * 4.0f));
		ldist.Distort(leftbufin, 
			parameterTable[cp_ChannelDistAmount], 
			parameterTable[cp_ChannelDistColor],
			parameterTableInt[cp_ChannelDistType]);
		Scale(rightbufin, (1 + parameterTable[cp_ChannelDistPreGain] * 4.0f));
		rdist.Distort(rightbufin, 
			parameterTable[cp_ChannelDistAmount], 
			parameterTable[cp_ChannelDistColor], 
			parameterTableInt[cp_ChannelDistType]);
	}
	FixDenormals(leftbufin);
	FixDenormals(rightbufin);
	lfilter.Render(leftbufin, 
		parameterTable[cp_ChannelFilterCutoff],
		parameterTable[cp_ChannelFilterResonance],
		parameterTableInt[cp_ChannelFilterType]);
	rfilter.Render(rightbufin, 			
		parameterTable[cp_ChannelFilterCutoff],
		parameterTable[cp_ChannelFilterResonance],
		parameterTableInt[cp_ChannelFilterType]);
	if (parameterTable[cp_ChannelPhaserAmount] > 0) {
		lphaser.Render(leftbufin,
			parameterTable[cp_ChannelPhaserFrequency],
			parameterTable[cp_ChannelPhaserFeedback],
			parameterTable[cp_ChannelPhaserAmount]);
		rphaser.Render(rightbufin,
			parameterTable[cp_ChannelPhaserFrequency],
			parameterTable[cp_ChannelPhaserFeedback],
			parameterTable[cp_ChannelPhaserAmount]);
	}
	switch (parameterTableInt[cp_ChannelCompressorMode]) {
	case 0:
		break;

	default:
		{
			comp.threshdB = -24+24*parameterTable[cp_ChannelCompressorThreshold];
			comp.ratio = parameterTable[cp_ChannelCompressorRatio];
			comp.setAttack(parameterTable[cp_ChannelCompressorAttack]*127);
			comp.setRelease(parameterTable[cp_ChannelCompressorRelease]*127);
			float makeup_gain = 1 + parameterTable[cp_ChannelCompressorMakeupGain];
			comp.Render(leftbufin, rightbufin, makeup_gain, parameterTableInt[cp_ChannelCompressorMode] == 2);
			break;
		}
	}
	PanVolume(left_out, right_out, leftbufin, rightbufin, 
		parameterTable[cp_ChannelVolume], 
		parameterTable[cp_ChannelPan]);
}

void Channel::NoteOn(int key, int vel) {
	Voice *voice = mixer.AllocVoice(this, key);
	if (voice) {
		voice->Play(this, key, vel, voice->GetChannel() == this && params[cp_Polyphony] == 1);
		lastNote=key;
	}
}

void Channel::NoteOff(int key) {
	mixer.StopVoice(this, key);
}

void Channel::ControllerChange(int cont, int value) {
	controllerValues[cont] = value;
}

void Channel::StopGroup(int group) {
	mixer.StopVoiceGroup(this, group);
}

}  // namespace